/*
 * Copyright (C) 2009 Kristopher T Babic
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of
 * the GNU General Public License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program;
 * if not, see <http://www.gnu.org/licenses>.
 */

package edu.idp.client.draw;

import edu.idp.client.interfaces.Drawable;
import edu.idp.client.interfaces.Storable;
import edu.idp.shared.StoredObject;
import edu.idp.shared.utils.Convert;

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.Calendar;
import java.util.LinkedList;

abstract class Drawing implements Storable, Drawable {
    protected Point firstPt = new Point(0, 0);
    protected Point lastPt = new Point(0, 0);
    protected Point curPt = new Point(0, 0);
    protected Graphics bufferGraphic;

    private Color color = Color.black;
    private boolean active = false;
    private boolean first = true;
    private int minx = 0, miny = 0, maxx = 0, maxy = 0;
    private Component component;
    private LinkedList ptList;
    private long timeDifference = 0;
    private long timeCreated = 0;
    private boolean drawToComp = true;
    private long timeDeath = StoredObject.DEFAULT_DEATH;//253401714000000l;
    private MouseAdapter mouseAdapter = null;
    private MouseMotionAdapter mouseMotionAdapter = null;

    public Drawing() {
        this((Component) null);
    }

    public Drawing(Component component) {
        if (component != null)
            setComponent(component);
        setGraphic((Graphics) null);
    }

    abstract public void drawCurrent(Graphics graphics);

    abstract public void drawEnd(Graphics graphics);

    abstract public void drawStart(Graphics graphics);

    abstract public int getType();

    abstract protected String storeExtra();

    abstract protected void restoreExtra(String S);

    abstract protected Rectangle getSubBounds(int min_x, int max_x, int min_y, int max_y);

    public boolean isActive() {
        return active;
    }

    public Point getLast() {
        return lastPt;
    }

    public void setActive(boolean b) {
        active = b;
    }

    public Point getCur() {
        return curPt;
    }

    //public Rectangle getBounds () { return bounds;  }

    public void setColor(Color c) {
        color = c;
    }

    public void setGraphic(Graphics g) {
        bufferGraphic = g;
    }

    public Graphics getGraphic() {
        return bufferGraphic;
    }

    public void setComponent(Component c) {

        if (component != null && component == c)
            return;

        if (mouseAdapter == null)
            mouseAdapter = new MyMouseAdapter();
        if (mouseMotionAdapter == null)
            mouseMotionAdapter = new MyMouseMotionAdapter();

        if (component != null) {
            component.removeMouseListener(mouseAdapter);
            component.removeMouseMotionListener(mouseMotionAdapter);
        }

        component = c;
        component.addMouseListener(mouseAdapter);
        component.addMouseMotionListener(mouseMotionAdapter);

/*
		component.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent event) {
				if(isActive()) {
System.out.print("MP ");
					resetList();
					setStart(event.getPoint());
					storePoint(event.getPoint());
				}
			}
			public void mouseClicked(MouseEvent event) {
///*
				(isActive()) {
System.out.print("MC ");
					setEnd(event.getPoint());
					storePoint(event.getPoint());
				}

      }
			public void mouseReleased(MouseEvent event) {
				if(isActive()) {
System.out.print("MR ");
					setEnd(event.getPoint());
					storePoint(event.getPoint());
          setCreationTime(Drawing.this.getTime());

				}
			}
		});

		component.addMouseMotionListener(new MouseMotionAdapter() {
			public void mouseDragged(MouseEvent event) {
				if(isActive()) {
System.out.print("MM");
					setMove(event.getPoint());
					storePoint(event.getPoint());
				}
			}
		});
  */
    }


    public void setStart(Point P) {

        firstPt.x = lastPt.x = curPt.x = minx = maxx = P.x;
        firstPt.y = lastPt.y = curPt.y = miny = maxy = P.y;

        if (drawToComp) {
            Graphics g = component.getGraphics();

            if (g != null) {
                drawCurrent(g);
                g.dispose();
            }
        }
        drawCurrent(bufferGraphic);

        first = true;
    }

    public void setEnd(Point P) {

        if (first) {
            firstPt.x = lastPt.x = curPt.x = P.x;
            firstPt.y = lastPt.y = curPt.y = P.y;
        }
        else {
            lastPt.x = curPt.x;
            lastPt.y = curPt.y;
            curPt.x = P.x;
            curPt.y = P.y;
        }

        if (P.x > maxx)
            maxx = P.x;
        else if (P.x < minx)
            minx = P.x;

        if (P.y > maxy)
            maxy = P.y;
        else if (P.y < miny)
            miny = P.y;

        if (drawToComp) {
            Graphics g = component.getGraphics();

            if (g != null) {
                drawEnd(g);
                g.dispose();
            }
        }
        drawEnd(bufferGraphic);

    }

    public void setMove(Point P) {

        lastPt.x = curPt.x;
        lastPt.y = curPt.y;
        curPt.x = P.x;
        curPt.y = P.y;

        if (P.x > maxx)
            maxx = P.x;
        else if (P.x < minx)
            minx = P.x;

        if (P.y > maxy)
            maxy = P.y;
        else if (P.y < miny)
            miny = P.y;

        if (drawToComp) {
            Graphics g = component.getGraphics();

            if (g != null) {
                drawCurrent(g);
                g.dispose();
            }
        }
        drawCurrent(bufferGraphic);
        first = false;
    }

    private void resetList() {
        if (ptList == null)
            ptList = new LinkedList();
        else if (ptList.size() > 0)
            ptList.clear();
    }

    private void storePoint(Point P) {
        ptList.addLast(P);
//System.out.println("("+P.x+","+P.y+")");
    }


    /**
     * ******
     * IMPORTANT:
     * need to add in the SERVER TIMER principle here.  need to implement it first
     * <p/>
     * *******
     */

    public StoredObject store() {

//		String S = new String();
        StoredObject so = new StoredObject();
        so.setType(this.getType());
        so.setColor(this.color);
        so.setDate(this.getCreationTime());
        so.setDeath(getDeathTime());
        so.setBounds(getBounds());
/* 2-10-00
		S += Convert.fromInt(this.getType());
		S += Convert.fromColor(this.color);
		//Calendar cal = Calendar.getInstance();
		S += Convert.fromLong(this.getCreationTime());
		S += Convert.fromLong((long)0);
 */
        String S = "";
        S += Convert.fromInt(ptList.size());

        for (int i = 0; i < ptList.size(); i++) {
            Point p = (Point) ptList.get(i);
            S += Convert.fromInt(p.x);
            S += Convert.fromInt(p.y);
        }
        S += storeExtra();
        so.setExtra(S);
        //S.add(this.getType());
        //S.add(this.color);
        //S.add(ptList.clone());

        return so;//S;
    }


    public void restore(StoredObject in) { //String in) {
        boolean start = true;

        try {

            setColor(in.getColor()); //DrawType.getColor(in));

            String listString = in.getExtra();  //DrawType.getExtra(in);

            LinkedList listLL = new LinkedList();

            int size = 0;

            if (listString != null && !listString.equals(""))
                size = Convert.toInt(listString.substring(0, 2)); //listString.length() / 2;

            this.drawToComp = false;
            int j = 2;
            //System.out.print("("+(2 + size*2 + 2)+"->"+listString.length()+"):size:"+size+" ");
            restoreExtra(listString.substring(2 + size * 4, listString.length()));

            if (size > 1) {
                for (int i = 0; i < size; i++) {
                    if (start) {
                        Point p = new Point(Convert.toInt(listString.substring(j, j + 2)),
                                            Convert.toInt(listString.substring(j + 2, j + 4)));
                        setStart(p);
                        listLL.addLast(p);
                        start = false;
                    }
                    else {
                        Point p = new Point(Convert.toInt(listString.substring(j, j + 2)),
                                            Convert.toInt(listString.substring(j + 2, j + 4)));
                        setMove(p);
                        listLL.addLast(p);
                    }
                    j += 4;
                }
            }


            Point p = new Point(Convert.toInt(listString.substring(j, j + 2)),
                                Convert.toInt(listString.substring(j + 2, j + 4)));
            setEnd(p);
            listLL.addLast(p);

            resetList();
            ptList = listLL;
        }
        catch (Exception e) {
        }
        this.drawToComp = true;

    }

    public Rectangle getBounds() {
        return getSubBounds(minx, maxx, miny, maxy);
//		return new Rectangle(getMin(minx-1), getMin(miny-1), maxx - minx + 2, maxy - miny + 2);
    }

    public Color getColor() {
        return color;
    }

    protected long getTime() {
        return Calendar.getInstance().getTime().getTime() + getTimeDifference();
    }

    public void setTimeDifference(long time) {
        this.timeDifference = time;
    }

    public long getTimeDifference() {
        return this.timeDifference;
    }

    public void setCreationTime(long time) {
        this.timeCreated = time;
    }

    public long getCreationTime() {
        return this.timeCreated;
    }

    public void setLifeSpan(long time) {
        this.timeDeath = time;
    }

    public long getLifeSpan() {
        return this.timeDeath;
    }

    protected long getDeathTime() {
        return ((getCreationTime() + getLifeSpan()) > 253401714000000l || (getCreationTime() + getLifeSpan()) < 0) ? 253401714000000l : getCreationTime() + getLifeSpan();
    }

    class MyMouseMotionAdapter extends MouseMotionAdapter {
        public void mouseDragged(MouseEvent event) {
            if (isActive()) {
//System.out.print("MM");
                Drawing.this.setMove(event.getPoint());
                Drawing.this.storePoint(event.getPoint());
            }
        }
    }

    class MyMouseAdapter extends MouseAdapter {

        public void mousePressed(MouseEvent event) {
            if (isActive()) {
//System.out.print("MP ");
                Drawing.this.resetList();
                Drawing.this.setStart(event.getPoint());
                Drawing.this.storePoint(event.getPoint());
            }
        }

        public void mouseClicked(MouseEvent event) {
/*
				(isActive()) {
System.out.print("MC ");
					setEnd(event.getPoint());
					storePoint(event.getPoint());
				}
*/
        }

        public void mouseReleased(MouseEvent event) {
            if (isActive()) {
//System.out.print("MR ");
                Drawing.this.setEnd(event.getPoint());
                Drawing.this.storePoint(event.getPoint());
                Drawing.this.setCreationTime(Drawing.this.getTime());

            }
        }


    }
}